#include "graph_test.h"
#include "dag_nodes.h"

#include <list>
#include <set>

using namespace dag;
using namespace std;

const char* GraphTest::description() {
	return "graph_test";
}

bool GraphTest::run() {
	return testLoops() && testNoninvertible() && testMissingHead() &&
	testMissingTail() && testMissingResource() && testUnconnected() && 
	testAmbiguousConnection() && testHangingConnection() && testGraphOp();
}

bool GraphTest::testLoops() {
	PassNode node1;
	PassNode node2;
	PassNode node3;
	PassNode node4;
	
	Graph graph;
	graph.setHeadNode(node1);
	graph.addNode(node2);
	graph.addNode(node3);
	graph.setTailNode(node4);
	FrameLink link = FrameLink(Link(), 
							   Link(), 
							   Link::fullyConnected(1), 
							   Link());
	
	graph.connect(Edge(&node1, link, &node2));
	graph.connect(Edge(&node2, link, &node3));
	graph.connect(Edge(&node3, link, &node4));
	graph.connect(Edge(&node3, link, &node2)); // add in a loop

	list<set<const Node*> > loops;
	dag::find_loops(graph, loops);
	
	if (loops.size() != 1) {
		return false;
	}
	
	if (loops.front().size() != 2) {
		return false;
	}
	
	if (loops.front().count(&node2) != 1) {
		return false;
	}
	
	if (loops.front().count(&node3) != 1) {
		return false;
	}
	
	return true;
}

bool GraphTest::testNoninvertible() {
	return true;
}

bool GraphTest::testMissingHead() {
	return true;
}

bool GraphTest::testMissingTail() {
	return true;
}

bool GraphTest::testMissingResource() {
	return true;
}

bool GraphTest::testUnconnected() {
	PassNode node1;
	PassNode node2;
	PassNode node3;
	PassNode node4;
	
	Graph graph;
	graph.setHeadNode(node1);
	graph.addNode(node2);
	graph.addNode(node3);
	graph.setTailNode(node4);
	FrameLink link = FrameLink(Link(), 
							   Link(), 
							   Link::fullyConnected(1), 
							   Link());
	
	graph.connect(Edge(&node1, link, &node2));
	graph.connect(Edge(&node2, link, &node4));
	
	list<GraphError> errors;
	FrameSpec in(0, 0, 1, 0);
	FrameSpec out(0, 0, 1, 0);
	validate(graph, in, out, errors);
	
	if (errors.size() != 1) {
		return false;
	}
	
	if (errors.front().getGraphErrorType() != GraphError_UnconnectedNode) {
		return false;
	}
	
	if (errors.front().getNode() != &node3) {
		return false;
	}
	
	return true;
	
}

bool GraphTest::testAmbiguousConnection() {
	PassNode node1;
	PassNode node2;
	PassNode node3;
	PassNode node4;
	
	Graph graph;
	graph.setHeadNode(node1);
	graph.addNode(node2);
	graph.addNode(node3);
	graph.setTailNode(node4);
	FrameLink link = FrameLink(Link(), 
							   Link(), 
							   Link::fullyConnected(1), 
							   Link());
	
	graph.connect(Edge(&node1, link, &node2));
	graph.connect(Edge(&node2, link, &node3));
	graph.connect(Edge(&node3, link, &node4));
	graph.connect(Edge(&node1, link, &node3));
	
	
	list<GraphError> errors;
	FrameSpec in(0, 0, 1, 0);
	FrameSpec out(0, 0, 1, 0);
	validate(graph, in, out, errors);
	
	if (errors.size() != 1) {
		return false;
	}
	
	if (errors.front().getGraphErrorType() != GraphError_AmbiguousConnection) {
		return false;
	}
	
	const vector<Edge*>& edgeVec = errors.front().getEdges();
	if (edgeVec.size() != 2) {
		return false;
	}
	
	for (size_t i = 0; i < edgeVec.size(); i++) {
		if (edgeVec[i]->from != &node1 && edgeVec[i]->from != &node2) {
			return false;
		}
		if (edgeVec[i]->to != &node3) {
			return false;
		}
	}	
	
	return true;
}

bool GraphTest::testHangingConnection() {
	PassNode node1;
	PassNode node2;
	PassNode node3;
	PassNode node4;
	
	Graph graph;
	graph.setHeadNode(node1);
	graph.addNode(node2);
	graph.addNode(node3);
	graph.setTailNode(node4);
	FrameLink link = FrameLink(Link(), 
							   Link(), 
							   Link::fullyConnected(1), 
							   Link());
	
	graph.connect(Edge(&node1, link, &node2));
	graph.connect(Edge(&node2, link, &node3));
	graph.connect(Edge(&node1, FrameLink(Link(),
										 Link(),
										 Link::fromConnections(1, 1, 1), 
										 Link()),
					   &node3));
	graph.connect(Edge(&node3, FrameLink(Link(),
										 Link(),
										 Link::fullyConnected(1),
										 Link()),
					   &node4));
	
	list<GraphError> errors;
	FrameSpec in(0, 0, 2, 0);
	FrameSpec out(0, 0, 2, 0);
	validate(graph, in, out, errors);
	
	if (errors.size() != 1) {
		return false;
	}
	
	if (errors.front().getGraphErrorType() != GraphError_HangingConnection) {
		return false;
	}
	
	if (errors.front().getNode() != &node3) {
		return false;
	}
	
	return true;
}

bool GraphTest::testGraphOp() {
	return true;
}